package com.toilet.easyvideofx.manager;

import com.google.gson.reflect.TypeToken;
import com.toilet.easyvideofx.api.response.*;
import com.toilet.easyvideofx.compent.RequestComponent;
import com.toilet.easyvideofx.domain.DownloadFileRecord;
import com.toilet.easyvideofx.mapper.DownloadFileRecordMapper;
import com.toilet.easyvideofx.model.FileListModel;
import com.toilet.easyvideofx.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * 文件逻辑层
 */

public class FileManager {


//    public List<DownloadFileRecord> getModel(int currentPage, int pageSize) {
//        SqlSession sqlSession = MyBatisUtil.getSqlSession();
//        DownloadFileRecordMapper fileRecordMapper = sqlSession.getMapper(DownloadFileRecordMapper.class);
//        List<DownloadFileRecord> downloadFileRecords = fileRecordMapper.selectPage(0, 10);
//        sqlSession.commit();
//        return downloadFileRecords;
//    }

    public void uploadFile(File selectedFile) throws NoSuchAlgorithmException {
        System.out.println("文件位置" + selectedFile.getAbsolutePath() + "文件大小为" + selectedFile.length());

        long startOffset = 0L;
        //从服务端获取上传的块的大小
        int getUploadSize = RequestComponent.get("/operation_file/file_chunk_size", Integer.class, null);

        AtomicInteger chunkSizeGet = new AtomicInteger(getUploadSize > selectedFile.length() ? (int) selectedFile.length() : getUploadSize);
        System.out.println("获取到的传输块大小为" + chunkSizeGet);
        // 总文件块数
        long getTotal = selectedFile.length() / chunkSizeGet.get();
        // 若不能整除，则总块数+1
        if (selectedFile.length() % chunkSizeGet.get() != 0) {
            getTotal++;
        }

        System.out.println("文件总数量为" + getTotal);

        MessageDigest mdDigest = MessageDigest.getInstance("MD5");
        String md5 = getFileMd5(selectedFile);
        Map<String, String> checkParams = new HashMap<>();
        checkParams.put("md5", md5);
        FileCheckResponse getCheck = RequestComponent.get("/operation_file/file_check", FileCheckResponse.class, checkParams);
        if (getCheck.getUploadResult()) {
            System.out.println(selectedFile.getAbsolutePath() + "文件已经上传");
            return;
        }
        //当前文件块
        AtomicLong chunkNumber = new AtomicLong(1L);
        while (startOffset < (selectedFile.length() + chunkSizeGet.get())) {
            try (RandomAccessFile randomAccessFile = new RandomAccessFile(selectedFile, "r")) {
                // 先偏移到文件的指定位置
                randomAccessFile.seek(startOffset);
                byte[] buffer = new byte[chunkSizeGet.get()];
                System.out.println("当前文件偏移量为：" + startOffset + " 当前文件块 " + chunkSizeGet);
                // 先读取文件
                randomAccessFile.read(buffer);
                startOffset += chunkSizeGet.get();
                String fileMd5 = getBytesMd5(buffer, mdDigest, chunkSizeGet.get());
                System.out.println("文件的md5的值为：" + fileMd5 + "当前文件块大小：" + buffer.length);

                //先检查文件是否存在
                Map<String, String> chunkParams = new HashMap<>();
                chunkParams.put("md5", fileMd5);
                // 检查文件块是否存在，存在则直接跳过上传
                FileCheckResponse checkResponse = RequestComponent.get("/operation_file/file_check", FileCheckResponse.class, chunkParams);
                if (checkResponse.getUploadResult()) {
                    System.out.println("文件存在，跳过");
                    //存在则跳过
                    continue;
                }

                Map<String, String> params = new HashMap<>();
                params.put("md5", md5);
                params.put("chunkNumber", String.valueOf(chunkNumber.getAndIncrement()));
                params.put("chunkSize", String.valueOf(getUploadSize));
                params.put("chunkTotal", String.valueOf(getTotal));
                params.put("fileSize", String.valueOf(selectedFile.length()));
                params.put("fileName", selectedFile.getName());

                BaseResponse<FileUploadResponse> uploaded = RequestComponent.uploadStream(params, buffer, "/operation_file/file_chunk_upload");
                System.out.println(uploaded.getData().getUploadResult());
                System.out.println(uploaded.getData().getUploadChunk());

                long getLength = startOffset + chunkSizeGet.get();
                if (getLength > selectedFile.length()) {
                    chunkSizeGet.set((int) (selectedFile.length() - startOffset));
                }
                System.out.println("当前块数为" + chunkNumber.get() + "当前文件块大小为：" + chunkSizeGet + "文件大小：" + startOffset);
            } catch (IOException e) {
                System.out.println(e);
            }
        }
    }

    /**
     * 分页查询上传文件
     *
     * @param pageSize
     * @param currentPage
     * @return
     */
    public List<FileListModel> queryFilePage(Long pageSize, Long currentPage) {
        Map<String, String> params = new HashMap<>();
        params.put("pageSize", String.valueOf(pageSize));
        params.put("currentPage", String.valueOf(currentPage));
        BasePageResponse<FileDetailResponse> page = RequestComponent.getPage("/operation_file/file_list",
                new TypeToken<BaseResponse<BasePageResponse<FileDetailResponse>>>() {
                }.getType(), params);
        List<FileDetailResponse> data = page.getData();
        return data.stream().map(response -> {
            FileListModel fileListModel = new FileListModel();
            fileListModel.setFileSize(response.getFileSize());
            fileListModel.setFileMd5(response.getFileMd5());
            fileListModel.setFileName(response.getFileName());
            fileListModel.setId(response.getId());
            return fileListModel;
        }).collect(Collectors.toList());
    }

    /**
     * 获取数组的md5值
     *
     * @param buffer
     * @param MD5
     * @return
     */
    private String getBytesMd5(byte[] buffer, MessageDigest MD5, int length) {
        MD5.update(buffer, 0, length);
        return new BigInteger(1, MD5.digest()).toString(16);
    }

    /**
     * 获取文件的MD5
     *
     * @param file
     * @return
     */
    private String getFileMd5(File file) {
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            MessageDigest MD5 = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fileInputStream.read(buffer)) != -1) {
                MD5.update(buffer, 0, length);
            }
            return new BigInteger(1, MD5.digest()).toString(16);
        } catch (IOException | NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }
}
